//===----------------------------------------------------------------------===//
//
//                         BusTub
//
// buffer_pool_manager_instance.cpp
//
// Identification: src/buffer/buffer_pool_manager.cpp
//
// Copyright (c) 2015-2021, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "buffer/buffer_pool_manager_instance.h"

#include "common/exception.h"
#include "common/macros.h"

namespace bustub {

BufferPoolManagerInstance::BufferPoolManagerInstance(size_t pool_size, DiskManager *disk_manager, size_t replacer_k,
                                                     LogManager *log_manager)
    : pool_size_(pool_size), disk_manager_(disk_manager), log_manager_(log_manager) {
  // we allocate a consecutive memory space for the buffer pool
  pages_ = new Page[pool_size_];
  page_table_ = new ExtendibleHashTable<page_id_t, frame_id_t>(bucket_size_);
  replacer_ = new LRUKReplacer(pool_size, replacer_k);

  // Initially, every page is in the free list.
  for (size_t i = 0; i < pool_size_; ++i) {
    free_list_.emplace_back(static_cast<int>(i));
  }

  // TODO(students): remove this line after you have implemented the buffer pool manager
  // throw NotImplementedException(
  //     "BufferPoolManager is not implemented yet. If you have finished implementing BPM, please remove the throw "
  //     "exception line in `buffer_pool_manager_instance.cpp`.");
}

BufferPoolManagerInstance::~BufferPoolManagerInstance() {
  delete[] pages_;
  delete page_table_;
  delete replacer_;
}

auto BufferPoolManagerInstance::NewPgImp(page_id_t *page_id) -> Page * {
    // page_id_t next_page;
    // next_page = this->next_page_id_.load();
    if(free_list_.size()==0&&this->replacer_->Size()==0){//空闲列表为空且LRU中的都是不可销毁的
      return nullptr;                                              //Size 返回的是可销毁的数量
    }
    frame_id_t f_id;
    if(this->free_list_.size()!=0){
      f_id = free_list_.front();
      free_list_.pop_front();
    }else if(this->replacer_->Size()!=0){
      this->replacer_->Evict(&f_id);
      //判断替换页是否有脏页
      this->page_table_->Remove(this->pages_[f_id].GetPageId()); //将这页的记录删除
      this->disk_manager_->WritePage(this->pages_[f_id].GetPageId(),this->pages_[f_id].GetData());//将数据写入磁盘
      pages_[f_id].ResetMemory();
      pages_[f_id].is_dirty_=false;
      pages_[f_id].pin_count_=0;
    }else{
      page_id=nullptr;
      return nullptr;
    }
    *page_id = this->AllocatePage();
    this->page_table_->Insert(*page_id,f_id);
    // 不需要再创建了，因为在初始化的过程中pages_已经new过了
    this->replacer_->RecordAccess(f_id); //默认是不可销毁的
    pages_[f_id].pin_count_++; // 这个页面的pin值需要增加，表明当前线程不希望这个页面被销毁，类似于share_ptr的计数器
    pages_[f_id].page_id_=*page_id; // 设置pageid
    
    return &pages_[f_id];
  }

auto BufferPoolManagerInstance::FetchPgImp(page_id_t page_id) -> Page * { 
    //先从pages中搜寻，如果找不到，就需要从磁盘中读取，并将放到pages中，如果pages中没有位置
    //且都不可销毁，则返回nullptr，否则就读取磁盘中的数据进行替换（或者从freelist中获取空白位置），
    frame_id_t frame_id;
    if(this->page_table_->Find(page_id,frame_id)){
      return this->pages_+frame_id;
    }
    if(this->free_list_.size()!=0){
      frame_id = this->free_list_.front();
      this->free_list_.pop_front();
    }else{//没有空位
      //需要从LRUK中置换页面
      if(this->replacer_->Size()==0){
        std::cout<<"replaceer size eq 0, return nullptr"<<std::endl;
        return nullptr;
      }
      this->replacer_->Evict(&frame_id);
      // this->pages_[frame_id]; 要驱逐的页面
      // 先判断是否在为脏页，是的话就先写入磁盘
      if(this->pages_[frame_id].is_dirty_){
        this->page_table_->Remove(this->pages_[frame_id].GetPageId());
        this->disk_manager_->WritePage(this->pages_[frame_id].GetPageId(),this->pages_[frame_id].GetData());
        pages_[frame_id].ResetMemory();
        pages_[frame_id].is_dirty_=false;
        pages_[frame_id].pin_count_=0;
      }
      //页面被驱逐之后，之前的数据需要清除

      //首先确定好当前页面应该存在于哪个地方
      this->page_table_->Insert(page_id,frame_id);
      this->replacer_->RecordAccess(frame_id); //默认是不可销毁的
      pages_[frame_id].pin_count_++; // 增加pin
      pages_[frame_id].page_id_=page_id; // 设置pageid
      
      //读取一个page
      this->disk_manager_->ReadPage(page_id,this->pages_[frame_id].GetData());
    }
    return this->pages_+frame_id;
  }

auto BufferPoolManagerInstance::UnpinPgImp(page_id_t page_id, bool is_dirty) -> bool { 
    // 需要调用LRUK的SetEvictable设置成可销毁
    //LRUK维护的是frame，需要通过page id找到frame id
    frame_id_t frame; 
    if(!this->page_table_->Find(page_id,frame)){
      return false;
    }
    if(this->pages_[frame].pin_count_==0){
      // return false;
      this->replacer_->SetEvictable(frame,true);
    }
    else{
      //此处需要加锁
      this->pages_[frame].pin_count_--;
      if(this->pages_[frame].pin_count_==0){//当没有人想要固定这个页面的时候，可以设置成可销毁
        this->replacer_->SetEvictable(frame,true);
      }
    }
    this->pages_[frame].is_dirty_=is_dirty;
    return true; 
  }

auto BufferPoolManagerInstance::FlushPgImp(page_id_t page_id) -> bool { 
  frame_id_t frame; 
  if(!this->page_table_->Find(page_id,frame)){
    return false;
  }
  disk_manager_->WritePage(page_id,this->pages_[frame].GetData());
  this->pages_[frame].is_dirty_=false;
  return true;
  }

void BufferPoolManagerInstance::FlushAllPgsImp() {}

auto BufferPoolManagerInstance::DeletePgImp(page_id_t page_id) -> bool { 
  frame_id_t frameId;
  if(!this->page_table_->Find(page_id,frameId))//页面不存在于缓存中，直接返回
    return true;
  if(this->pages_[frameId].pin_count_>0)
    return false;
  // this->replacer_->Evict(frameId);
  this->replacer_->Remove(frameId);
  this->pages_[frameId].ResetMemory();
  this->pages_[frameId].is_dirty_=false;
  this->pages_[frameId].pin_count_=0;
  this->free_list_.push_back(frameId);
  this->page_table_->Remove(page_id);
  return false; 

}

auto BufferPoolManagerInstance::AllocatePage() -> page_id_t { return next_page_id_++; }

}  // namespace bustub
